/* Copyright 2017, 2018, 2019, 2020 Gabriel Czernikier
 *
 * This file is part of Côtehaus.
 * Côtehaus is free software: you can redistribute it and/or modify
 * it under the terms of one of the following:
 * 1. The same dual license as that of Côtehaus itself, of which individual licenses are
 * the MIT License and the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 2. The MIT License.
 * 3. The GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * Côtehaus is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * MIT License and the GNU General Public License for more details.
 * You should have received a copy of the MIT License and the GNU General Public License
 * along with Côtehaus.  If not, see <https://opensource.org/licenses/MIT> and <http://www.gnu.org/licenses/>.
 */
#include<stdbool.h>
#include<stdlib.h>
LIST_HEAD(method_id_head);  // struct method_id_st
unsigned long method_id_list_size = 0;

struct method_id_st *add_method_id(struct type_id_st *class_idx, struct proto_id_st *proto_idx, struct str_id_st *name_idx) {
	struct method_id_st *method_id = (struct method_id_st*)malloc(sizeof(struct method_id_st));
	method_id->class_idx = class_idx;
	method_id->proto_idx = proto_idx;
	method_id->name_idx = name_idx;
	method_id->external = class_idx->cotehaus_class_def->external;
	list_add(&method_id->method_id_lst, &method_id_head);
	method_id_list_size++;
	return method_id;
}

struct omethod_id_st {
  struct method_id_st *method_id;
  struct omethod_id_st *next_representative;
} *omethod_id_ary;

int omethod_id_st_compar(struct omethod_id_st *omethod_id_1, struct omethod_id_st *omethod_id_2) {
	if(oproto_id_1->proto_id->return_type_idx!=oproto_id_2->proto_id->return_type_idx) {
		return oproto_id_1->proto_id->return_type_idx-oproto_id_2->proto_id->return_type_idx;
	}
	if(omethod_id_1->class_idx->idx!=omethod_id_2->class_idx->idx)
		return omethod_id_1->class_idx->idx-omethod_id_2->class_idx->idx;
	if(omethod_id_1->name_idx->idx!=omethod_id_2->name_idx->idx)
		return omethod_id_1->name_idx->idx-omethod_id_2->name_idx->idx;
	return omethod_id_1->proto_idx->idx-omethod_id_2->proto_idx->idx;
	return 0;
}

void build_omethod_id() {
	omethod_id_ary = malloc(method_id_list_size*sizeof(struct omethod_id_st));
	memset(omethod_id_ary, 0, method_id_list_size*sizeof(struct omethod_id_st));
	struct list_head *tmp;
	unsigned int i=0;
	list_for_each(tmp, &method_id_head) {
		(omethod_id_ary+i)->method_id = (struct method_id_st *)list_entry(tmp, struct method_id_st, method_id_list);
		i++;
	}
	qsort(omethod_id_ary, method_id_list_size, sizeof(struct omethod_id_st), (int (*)(const void *, const void *))omethod_id_st_compar);
	i=1;
	unsigned int major_i = 0, major_idx = 0;
	while(major_i<method_id_list_size) {
		(omethod_id_ary+major_i)->method_id->idx = major_idx;
		while(i!=method_id_list_size && omethod_id_st_compar(omethod_id_ary+major_i, omethod_id_ary+i)==0) {
			(omethod_id_ary+i)->method_id->idx = (omethod_id_ary+major_i)->method_id->idx;
			i++;
		}
		if(i!=method_id_list_size)
			(omethod_id_ary+major_i)->next_representative = omethod_id_ary+i;
		major_i=i++;
		major_idx++;
	}
	bounds_move(NH_METHOD_ID_IDX, (2*sizeof(uint16_t))+sizeof(uint32_t)/*Storage Designators: ((struct type_id_st)(struct method_id_st).class_idx).idx==uint16_t,
	((struct proto_id_st)(struct method_id_st).proto_idx).idx==uint16_t,
	((struct str_id_st)(struct method_id_st).name_idx).idx==uint32_t,
	*/ *major_idx);
	majors_size_ary[NH_METHOD_ID_IDX] = major_idx;
}

void pack_omethod_id() {
	for(struct omethod_id_st *omethod_id_representative = omethod_id_ary;omethod_id_representative!=NULL; omethod_id_representative=omethod_id_representative->next_representative){
		// skip items collected during parsing of external refs
		if(omethod_id_representative->method_id->external)
			continue;
		uint16_t packed16 = htole16(omethod_id_representative->method_id->class_idx->idx);
		BUFFER_WRITE(buffer[NH_METHOD_ID_IDX],buf_len[NH_METHOD_ID_IDX],&packed16,buf_offset[NH_METHOD_ID_IDX],sizeof(uint16_t))
		packed16 = htole16(omethod_id_representative->method_id->proto_idx->idx);
		BUFFER_WRITE(buffer[NH_METHOD_ID_IDX],buf_len[NH_METHOD_ID_IDX],&packed16,buf_offset[NH_METHOD_ID_IDX],sizeof(uint16_t))
		uint32_t packed = htole32(omethod_id_representative->method_id->name_idx->idx);
		BUFFER_WRITE(buffer[NH_METHOD_ID_IDX],buf_len[NH_METHOD_ID_IDX],&packed,buf_offset[NH_METHOD_ID_IDX],sizeof(uint32_t))
	}

}
